package p05;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;

import java.io.IOException;
import java.util.Set;

/**
 * BeanFactory后处理器ConfigurationClassPostProcessor是如何解析@Bean的呢?
 * 原理跟@ComponentScan注解解析差不多
 * ClassPathResource 可以直接得到类路径下的资源
 *
 * @author Chen weishuai
 * @since 2025/7/29
 */
public class ResolveBeanAnnotation {

    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean(MyResolveBeanAnnotation.class);
        context.refresh();
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }


    static class MyResolveBeanAnnotation implements BeanFactoryPostProcessor {
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            DefaultListableBeanFactory context = (DefaultListableBeanFactory) beanFactory;
            // 先注册进来再说 :解析@Bean时，要保证所在的Config类被加入容器，因为@Bean会把Config类当做工厂来创建bean.即创建@Bean注解标注的bean时，创建的bean的工厂就是Config类
            context.registerBeanDefinition("config", new RootBeanDefinition(Config.class));

            CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();

            try {
                MetadataReader reader = factory.getMetadataReader(new ClassPathResource("p05/config.class"));
                // 得到该配置类中所有被@Bean标注的方法信息
                Set<MethodMetadata> annotatedMethods = reader.getAnnotationMetadata()
                        .getAnnotatedMethods(Bean.class.getName());

                for (MethodMetadata method : annotatedMethods) {
                    // 得到该方法@Bean注解initMethod属性值（一个方法存在标注了多个注解情况，比如@Bean 、@PostConstruct等）
                    String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
                    // 为该方法创建一个bean定义
                    BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition();
                    // 通过工厂方式注册bean
                    beanDefinitionBuilder.setFactoryMethodOnBean(method.getMethodName(), "config");
                    // 设置模式
                    beanDefinitionBuilder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
                    if (initMethod.length() > 0) {
                        beanDefinitionBuilder.setInitMethodName(initMethod);
                    }
                    AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
                    // 注册bean定义
                    context.registerBeanDefinition(method.getMethodName(), beanDefinition);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
